package xyz.kongbai121.common.utils;

import cn.hutool.crypto.SecureUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import xyz.kongbai121.common.exception.RRException;

import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.*;

@Slf4j
public class SignHelper {

    public static final String KEY_ALGORTHM = "RSA";
    public static final String SIGNATURE_ALGORITHM = "SHA256WithRSA";

    /**
     * 处理参数排序后拼接
     *
     * @param object        入参数对象
     * @param skipKey       需要跳过的字段 如: sign
     * @param keyValueJoin  键跟值之间用什么连接 如(=): k=v
     * @param paramJoin     键值对之间用什么连接 如(&): k=v&k1=v1
     */
    public static String handleParam(Object object, String skipKey, String keyValueJoin, String paramJoin){
        keyValueJoin = keyValueJoin == null ? "" : keyValueJoin;
        paramJoin = paramJoin == null ? "" : paramJoin;
        String jsonStr = JSON.toJSONString(object);
        TreeMap<String, String> treeMap = JSON.parseObject(jsonStr, new TypeReference<TreeMap<String, String>>() {});
        Set<Map.Entry<String, String>> entries = treeMap.entrySet();
        Iterator<Map.Entry<String, String>> iterator = entries.iterator();
        List<String> values = Lists.newArrayList();
        while (iterator.hasNext()) {
            Map.Entry<String, String> entry = iterator.next();
            String k = entry.getKey();
            String v = entry.getValue();
            if (!PublicUtils.isBlank(k) && !k.equals(skipKey) && !PublicUtils.isBlank(v) && !"null".equals(v)) {
                values.add(k + keyValueJoin + v);
            }
        }
        return StringUtils.join(values, paramJoin);
    }


    /**
     * 校验签名
     */
    public static void verifySign(Object signObject, String sign){
        String handleParam = Constant.SIGN_SALT + handleParam(signObject, "sign", "=", "&") + Constant.SIGN_SALT;
        log.info("生成前置加密串handleParam: {}", handleParam);
        if (!SecureUtil.md5(handleParam).equals(sign)) {
            throw new RRException("加密串校验失败!");
        }
    }

    /**
     * 用私钥对信息生成数字签名
     *
     * @param bizContent 数据
     * @param privateKey 私钥
     */
    public static String sign(String bizContent, String privateKey) throws Exception {
        //解密私钥
        byte[] keyBytes = decryptBASE64(privateKey);
       // byte[] keyBytes = privateKey.getBytes();
        
        //构造PKCS8EncodedKeySpec对象
        PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(keyBytes);
        //指定加密算法
        KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORTHM);
        //取私钥匙对象
        PrivateKey privateKey2 = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
        //用私钥对信息生成数字签名
        Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
        signature.initSign(privateKey2);
        signature.update(bizContent.getBytes());
 
        return encryptBASE64(signature.sign());
    }

    /**
     * 校验数字签名
     *
     * @param bizContent 数据
     * @param publicKey  公钥
     * @param sign       数字签名
     */
    public static boolean verify(String bizContent, String publicKey,
                                 String sign) throws Exception {
        return verify(bizContent, publicKey, sign, KEY_ALGORTHM, SIGNATURE_ALGORITHM);
    }

    /**
     * 校验数字签名
     *
     * @param bizContent            数据
     * @param publicKey             公钥
     * @param sign                  数字签名
     * @param keyAlgorithm          签名方式
     * @param signatureAlgorithm    签名算法
     */
    public static boolean verify(String bizContent, String publicKey,
                                 String sign, String keyAlgorithm, String signatureAlgorithm) throws Exception {
 
        if (StringUtils.isBlank(publicKey) || StringUtils.isBlank(sign)) {
            return false;
        }
 
        //解密公钥
        byte[] keyBytes = decryptBASE64(publicKey);
        //构造X509EncodedKeySpec对象
        X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(keyBytes);
        //指定加密算法
        KeyFactory keyFactory = KeyFactory.getInstance(keyAlgorithm);
        //取公钥匙对象
        PublicKey publicKey2 = keyFactory.generatePublic(x509EncodedKeySpec);
 
        Signature signature = Signature.getInstance(signatureAlgorithm);
        signature.initVerify(publicKey2);
        signature.update(bizContent.getBytes());
        //验证签名是否正常
        try {
            return signature.verify(decryptBASE64(sign));
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * BASE64解密
     * @param key = 需要解密的密码字符串
     */
	public static byte[] decryptBASE64(String key) throws Exception {
		return (new BASE64Decoder()).decodeBuffer(key);
	}

    /**
     * BASE64解密
     * @param key = 需要解密的密码字符串
     */
	public static String decryptBASE64ToString(String key) throws Exception {
		return new String((new BASE64Decoder()).decodeBuffer(key), StandardCharsets.UTF_8);
	}

	/**
     *  BASE64加密
     *  @param key = 需要加密的字符数组
     */
	public static String encryptBASE64(byte[] key) throws Exception {
		return (new BASE64Encoder()).encodeBuffer(key);
	}

    /**
     * 把二进制转化为大写的十六进制表示
     */
    public static String byte2hex(byte[] bytes) {
        StringBuilder sign = new StringBuilder();
        for (int i = 0; i < bytes.length; i++) {
            String hex = Integer.toHexString(bytes[i] & 0xFF);
            if (hex.length() == 1) {
                sign.append("0");
            }
            sign.append(hex.toUpperCase());
        }
        return sign.toString();
    }
}